#!/usr/bin/python3

import re
import random
import shutil
import os
import json
import time

import bili.helper as helper

from bili.state import GlobalState
from bili.spider import SpiderAid

from bili.constants import *


class bili(object):
    SUBID=defaultSUBID

    def __init__(self,globalState:GlobalState,dir_for_result:str):
        self.globalState=globalState

        # videos/store/bili
        self.dir_for_result=dir_for_result

        self.spiderAid=SpiderAid()

    def gen_vod_play_url(self,data,aid):
        vod_play_url=data.get("vod_play_url",None)
        vod_play_url_try=data.get("vod_play_url_try",0)

        if vod_play_url is None or len(vod_play_url)==0:
            if vod_play_url_try>10:
                print("[W] vod_play_url_try > 10: aid="+aid)
                return
            vod_play_url=self.spiderAid.fetch_vod_play_url(aid)
            data["vod_play_url"]=vod_play_url
            data["vod_play_url_try"]=vod_play_url_try+1
        
        data["vod_play_url_fetch"]="/api/onlinevod/from_bili/"+aid

        saved=self.gen_vod_play_url_path(aid,False)
        if not os.path.exists(saved):
            try:
                if len(vod_play_url)>0:
                    with open(saved,"w") as f:
                        f.write(vod_play_url)
            except Exception as e:
                print("[E] save vod_play_url error",e)

    # def __del__(self):
    #     # 析构函数
    #     print("[i] save data")
    #     self.globalState.save()

    def __get_path(self,aid,suffix,useSubDir=True):
        if useSubDir:
            # sub 中只存放 detail
            return os.path.join(self.dir_for_result,self.SUBID,"{}.{}".format(aid,suffix))
        # 图片, vod_play_url 放在上一层目录!!!
        return os.path.join(self.dir_for_result,"{}.{}".format(aid,suffix))

    def _get_detail_path(self,aid):
        return self.__get_path(aid,"json",True)
        # "{}".format()
        # return "{}/{}.json".format(self.dir_for_result,aid)

    def pre_save(self,data,aid):
        self.gen_vod_play_url(data,aid)
        data["vod_id"]=self.gen_vod_id(aid)
        data["vod_pic"]=self.gen_pic_path(aid)

    def save_detail(self,data,aid):
        fp=self._get_detail_path(aid)
        # print(fp)
        data["vod_play_from"]=FROM_BILIBILI
        
        with open(fp,"w") as f:
            json.dump(data,f)

    def gen_vod_id(self, aid,isUrl=True):
        pre=""
        if isUrl:
            pre=URL_FILE
        return "{}{}".format(pre,self._get_detail_path(aid))
        # return "/files/videos/{}/{}/{}.json".format(DIR_FOR_RESULT,self.SUBID,aid)
        # return aid
        
    def gen_pic_path(self,aid,isUrl=True):
        # pic 放在 上层目录!!
        pre=""
        if isUrl:
            pre=URL_FILE
        return "{}{}".format(pre,self.__get_path(aid,"jpg",False))
    
    def gen_vod_play_url_path(self,aid,isUrl=True):
        # pic 放在 上层目录!!
        pre=""
        if isUrl:
            pre=URL_FILE
        return "{}{}".format(pre,self.__get_path(aid,"txt",False))

    
    
    def get_aid_by_bvid(self,bvid:str,need_cid=False):
        print("get_aid_by_bvid, bvid=",bvid)
        aid=self.globalState.getAid(bvid)
        if aid is not None:
            if not need_cid:
                return aid
            else:
                cid=self.globalState.getCid(aid)
                if cid is not None:
                    return aid,cid
                # get cid
        # else:
        #     # get aid , cid

        aid,cid=self.spiderAid.get_aid_cid_by_bvid(bvid)
        if aid is None:
            if need_cid:
                return None,None
            return None
        self.globalState.setAid(bvid,aid)
        if cid is not None:
            # aid : cid 不是 1v1! 是 1 vs many
            self.globalState.setCid(aid,cid)
        if need_cid:
            return aid,cid
        return aid

        
    # def get_cid(self,aid:str):
    #     # aid : cid 不是 1v1! 是 1 vs many
    #     cid=self.globalState.getCid(aid)
    #     if cid is not None:
    #         return cid
    #     cid=self.spiderAid.get_cid_by_aid(aid)
    #     if cid is None:
    #         return None
    #     self.globalState.setCid(cid)
    #     return cid
    
    def load_data(self,aid):
        jo_path=self._get_detail_path(aid)
        try:
            with open(jo_path,"r") as jo_file:
                data=json.load(jo_file)
            if data.get("vod_pic",None) is not None:
                # self.spiderAid.ge
                return data
        except:
            pass

        # 包含 pic下载
        data=self.spiderAid.fetch_info_by_aid(self.dir_for_result,aid)
        if data is not None:
            with open(jo_path,"w") as jo_file:
                json.dump(data,jo_file)
            return data
        return None



# BiliWork

############# 同个aid组成的播放列表(同一个vod)
# main_bv_av
############# 合集: 由不同的aid组成的播放列表(同一个vod) , 这个视频信息 是由js手动弄的.
# main_episodes
############# 影视: season_id获取的播放列表(同一个vod)
# main3_bangumi

class AVBV(bili):
    def __init__(self,globalState:GlobalState,dir_for_result:str):
        super().__init__(globalState,dir_for_result)

    def _match(self,path,f):
        if not os.path.isfile(path):
            return False
        if not (f.startswith("bvids.") and f.endswith(".txt")):
            return False
        if helper._get_file_size(path)<=0:
            return False
        print(" [i] f_bvids=",f)
        return True

    def start(self,dir,subid):
        if subid is None:
            self.SUBID=defaultSUBID
        else:
            self.SUBID=subid

        # main_bv_av
        print("  [INFO] bvids.*.txt")
        fffs=os.listdir(dir) # list 当前目录.
        fffs.sort()
        print(dir)
        print(fffs)
        print(dir,os.path.exists(dir))
        for i in fffs:
            fi=os.path.join(dir,i)
            if not self._match(fi,i):
                # print("not match",i)
                continue
            print(fi,os.path.exists(fi))
            try:
                with open(fi,"r") as f:
                    while True:
                        line=f.readline()
                        if not line:
                            # line 都是 "\n结尾或者末行==文字"
                            break
                        self._process(line)
            except Exception as e:
                print(e)



    def _process(self,line:str):
        if not line:
            return
        line=line.strip(" /\r\n")
        if line.startswith("#"):
            return

        print("---> process : ",line)

        if line[:10].lower().startswith("https://"):
            end=line.find("?")
            if end>0:
                line=line[:end]
            line=line.removeprefix("https://www.bilibili.com/video/")

        line=line.strip(" /")
        line=line.split("/")[-1]
        line=line.strip()

        if len(line)==0:
            return

        if self.globalState.isDone(line):
            print("[重复] chong fu. 已经 获取过了: ",line)
            return True

        # elif line.startswith("https://"):
        #     line.removeprefix("https://www.bilibili.com/video/")
        #     video/BV1Hs411X7pH/

        lower=line.lower()
        if lower.startswith("bv"):
            aid=self.get_aid_by_bvid(line)
        elif lower.startswith("av"):
            aid=line[2:]
        else:
            aid=line
        if len(aid)==0:
            return
        
        if self.globalState.isDone(aid):
            print("[重复] chong fu. 已经 获取过了: ",aid)
            return True
        self.globalState.addDone(line,aid)
        print("aid=",aid)
        if not aid or aid.startswith("#"):
            return

        data=self.load_data(aid)
        # if data is None:
        #     data=helper.fetch_info(self.dir_for_result,aid) # 包含图片下载了. 并且保存了json数据.

        # TODO: check pic # ok=check_pic(aid)
        if data is None:
            print("== == == == ==> NO data found",aid,line)
            return
        
        self.pre_save(data,aid)
        self.save_detail(data,aid)
        # TODO -> db:detail

        time.sleep(0.5*random.random())

        return True
        
        # ## ?? 这里没有 保存vod? TODO
        # dst="__poster.jpg"
        # if helper._get_file_size(dst)<=0: # 分组的封面 只保存第一个 有效图片
        #     src=data["vod_pic"]
        #     shutil.copyfile(src, dst)


# 这个 自带json
class Episodes(bili):
    cookies=None
    def __init__(self,globalState,dir_for_result):
        super().__init__(globalState,dir_for_result)

    # def main_episodes():
    #     ############# 合集: 由不同的aid组成
    #     d="episodes.bili"
    #     gen_episodes(d)
    def start(self,dir,subid):
        if subid is None:
            self.SUBID=defaultSUBID
        else:
            self.SUBID=subid
    
        ############# 合集: 由不同的aid组成
        #     "episodes.bili"
        # 或  "xxx.episodes.bili"
        # 或  "episodes.bili.xxx"
        dname="episodes.bili"
        suffix="."+dname
        prefix=dname+"."
        for d in os.listdir(dir):
            if d.endswith(suffix) or d.startswith(prefix) or d==dname:
                self.gen_episodes(dir,d)
       

    def gen_episodes(self,parent,d):
        dir=os.path.join(parent,d)
        if not os.path.isdir(dir):
            return
        # d,ff
        for _,_,ffs in os.walk(dir):
            ffs.sort()
            break
        for ff in ffs:
            if not ff.endswith(".json"):
                continue
            ffx=os.path.join(dir,ff)
            print("  ->",ffx)

            try:
                print("    -> -> ",ffx)
                with open(ffx,"r") as f:
                    data=json.load(f)
                pic=""
                aid=""
                videoList=[]
                count=1
                vod_pic=""
                for x in data.get("vods",[]):
                    url=None
                    if not pic:
                        pic=x.get("pic","")
                    if not aid:
                        aid=str(x.get("aid",""))

                    bvid=x.get("bv_id") # 没有 aid,cid时才有这个 bvid
                    if bvid is not None:
                        # get from cache, get from html
                        aid,cid=self.get_aid_by_bvid(bvid,True)
                        #print(f"aid={aid},cid={cid}")
                        #input("??? pause: ")
                        if aid is not None and cid is not None:
                            # /api/onlinevod/from_bili/376824836/374559739  /from_bili
                            url="{} {}$/api/onlinevod/from_bili/{}/{}".format(count, x.get("title",""), aid, cid)
                    else:
                        if aid != "":
                            url="{} {}$/api/onlinevod/from_bili/{}/{}".format(count,x.get("title",""),x.get("aid",""),x.get("cid",""))
                        
                    if url is None:
                        continue
                    videoList.append(url)
                    count+=1
                if not aid:
                    print("  no aid found ? ",ffx)
                    continue
                if pic:
                    out=self.spiderAid.download_pic(self.dir_for_result,pic,str(aid))
                if aid:
                    vod_pic="{}.jpg".format(aid)

                # ROOTPATH,d,ff
                # path="{}/aid.{}+.json".format(self.ROOTPATH,aid)
                vod_id="from_bilibili/{}".format(aid)
                # os.path.join(self.dir_for_result,) 
                
                # vod | data | detail
                data={
                    #/api/onlinevod/from_bilibili/aid/cid
                    #/files/videos/store_bili/aid.json
                    #/files/videos/store_bili/aid.jpg
                    "vod_id": vod_id,  
                    
                    "vod_name": "合集:"+data.get("title","?"),
                    "vod_pic": pic, #vod_pic,
                    "vod_tag" : "file",
                    "vod_director":data.get("owner","bili"),
                    "vod_play_url":"#".join(videoList),
                    "vod_remarks": "b站", # 浏览量 不要了...
                    "type_name": "bili", # 视频内容 类别 不要了
                    "vod_content": "----", # 描述不要了
                    # "type_name": v_type, # 视频内容 类别 不要了
                    # "vod_content": desc, # 描述不要了
                }
                # TODO -> db:detail
                print("[INFO] ower=",data.get("vod_director"),"vod_name=",data.get("vod_name"))
                self.pre_save(data,aid)
                self.save_detail(data,aid)
                return data
            except Exception as e:
                print("[E]", d , ff)
                print(e)

class Bangumi(bili):
   
    def __init__(self,globalState,dir_for_result):
        super().__init__(globalState,dir_for_result)
        self.worker_AVBV=AVBV(globalState,dir_for_result)


    def start(self,dir,subid):
        print("[Bangumi0]",dir,subid)
        if subid is None:
            self.SUBID=defaultSUBID
        else:
            self.SUBID=subid
        self.worker_AVBV.SUBID=self.SUBID

        for d in os.listdir(dir):
            d2=os.path.join(dir,d)
            if not os.path.isfile(d2):
                continue
            if not (d.startswith("bangumi.") and d.endswith(".txt")):
                continue
            
            try:
                print("[Bangumi]",d2)
                with open(d2,"r") as f:
                    while True:
                        line=f.readline()
                        if not line:
                            # line 都是 "\n结尾或者末行==文字"
                            break
                        self.process_bangumi(line)
            except :
                pass

    
    def load_data(self,tmp_id):
        jo_path=self._get_detail_path(tmp_id)
        updated=False
        try:
            with open(jo_path,"r") as jo_file:
                data=json.load(jo_file)
            if data.get("vod_pic",None) is not None:
                if data.get("vod_pic","").startswith("http"):
                    # download image
                    isPicDownloaded=self.spiderAid.download_pic(self.dir_for_result,data.get("vod_pic",""),tmp_id,True)
                    # if success : change vod_pic:
                    if isPicDownloaded:
                        data["vod_pic"]=self.gen_pic_path(tmp_id)
                        updated=True
                    pass
                return data,updated
        except:
            pass
        return None,False


    def process_bangumi(self,url):
        # ssid=self.get_seasonid_by_url(line)
        # self._get_season_VideoList(ssid)

        url=url.strip(" /\n")
        print("---> process_bangumi : ",url)
        if(url.startswith("#")):
            return
        if not url.startswith("https://"):
            return
        
        end=url.find("?")
        if end>0:
            url=url[:end]
        url=url.strip(" \n/")

        if len(url)==0:
            return
        
        if self.globalState.isDone(url):
            print("[重复] chong fu. 已经 获取过了: ",url)
            return

        if url.find("bangumi/play")<0:
            print("[i] try to follow 301")
            url2=""
            try:
                url2=self.globalState.getFollow301(url)
                if not url2:
                    print("      by fetch")
                    url2=self.spiderAid.fetch(url).url
                    end=url2.find("?")
                    if end>0:
                        url2=url2[:end]
                    if url2.find("bangumi/play")>0:
                        self.globalState.setFollow301(url,url2)
            except Exception as e:
                print("[E] fetch error",e)

            time.sleep(0.5+random.random()*0.5)

            if url2.find("bangumi/play")>0:
                self.process_bangumi(url2) ## 递归.
            else:
                # BV id
                print("   **?    can't 301 , try BVXXXx | avxxxx")
                if self.worker_AVBV._process(url2):
                    self.globalState.setFollow301(url,url2)

                # TODO:
                # self.process(url) ## TODO?!????

            self.globalState.addDone(url)
            return
        self.globalState.addDone(url)

        season_id=self.get_seasonid_by_url(url)
        if not season_id:
            print("season_id : not get!!!!")
            return
        tmp_id="{}{}".format(SEASON_PREFIX,season_id)
        print("season_id:",tmp_id)
        if self.globalState.isDone(tmp_id): # doneStore.get(tmp_id):
            print("   [i] 重复 chong fu:", tmp_id)
            return

        self.globalState.addDone(tmp_id)
        
        # data,ok_pic=getData_andCheckPic(tmp_id)
        data,hasUpdated=self.load_data(tmp_id)
        if data is not None:
            if hasUpdated:
                data["vod_play_url_fetch"]="/api/onlinevod/from_bili/"+tmp_id
                self.save_detail(data,tmp_id)
            return

        if data is None:
            vod0=self._get_season_VideoList(tmp_id)
            hasUpdated=True
            if not vod0:
                return
            isPicDownloaded=self.spiderAid.download_pic(self.dir_for_result,vod0.get("vod_pic",""),tmp_id,True)
            data={
                "vod_id": self.gen_vod_id(tmp_id),#"from_bilibili/{}/${}$".format(tmp_id,ROOTPATH),# season_id_xxxx
                "vod_name": vod0.get("vod_name","tv"),
                "vod_pic": self.gen_pic_path(tmp_id) if isPicDownloaded else vod0.get("vod_pic",""),
                "vod_tag" : "file",
                "vod_director":"bili",
                # "vod_play_url":"#".join(videoList),
                "vod_remarks": vod0.get("vod_remarks","b站"),# "b站", # 浏览量 不要了...
                "type_name": vod0.get("type_name",""), # 视频内容 类别 不要了
                "vod_area":vod0.get("vod_area",""),
                "vod_content": vod0.get("vod_content",""), # 描述不要了
                "vod_play_url_fetch":self.gen_pic_path(tmp_id),
            }

            data["vod_play_url_fetch"]="/api/onlinevod/from_bili/"+tmp_id
            self.save_detail(data,tmp_id)

        # # else:
        # #     vod_id=data.get("vod_id","")
        # #     if vod_id.endswith("$"):
        # #         data["vod_id"]="from_bilibili/{}/${}$".format(tmp_id,ROOTPATH)
        # #         hasUpdated=True
        # #     else:
        # #         print("[i] 已经存在: {}".format(tmp_id))    


        # if hasUpdated:
        #     self.save_detail(vod,tmp_id)
        #     # save_as_json(vod, tmp_id+".json")
        #     # print("  season json saved: ",tmp_id+".json")

        # # return True



    # &quot;season_id&quot;:42282,&quot;
    pattern_seasonid_Pre="&quot;season_id&quot;:"
    pattern_seasonid_Suffix=",&quot"
    seasonid_Pattern=re.compile("{}\d+?{}".format(pattern_seasonid_Pre,pattern_seasonid_Suffix))

    def get_seasonid_by_url(self,url):
        seasonid=self.globalState.getSeasonid(url)
        if seasonid:
            return seasonid
           

        if url.startswith("https://www.bilibili.com/bangumi/play/ss"):
            ss=url.removeprefix("https://www.bilibili.com/bangumi/play/ss")
            if re.match("^\d+",ss):
                # 这个不用存. seasonidStore .  id就在url上!!!
                return ss
            
        try:
            text=self.spiderAid.fetch(url).text
            match=self.seasonid_Pattern.search(text)
            print("text len=",len(text),"match:",match)
            if match:
                # todo
                seasonid=text[match.start()+len(self.pattern_seasonid_Pre):match.end()-len(self.pattern_seasonid_Suffix)]
                self.globalState.setSeasonid(url,seasonid) # seasonidStore[url]=seasonid
                return seasonid
            else:
                return None
        except Exception as e:
            print("[W] get aid err",e)
            return None
    
    def _get_season_VideoList(self,season_id):
        print(" 剧集:  _get_season_VideoList ")
        if self.globalState.isError(season_id):
            return None
        try:
            sid=season_id.removeprefix(SEASON_PREFIX)
            url = "http://api.bilibili.com/pgc/view/web/season?season_id={0}".format(sid)
            rsp = self.spiderAid.fetch(url)
            jRoot = json.loads(rsp.text)
            jo = jRoot['result']
            id = jo.get('season_id',season_id)
            title = jo.get('title',"")
            pic = jo.get('cover',"")
            typeName = jo.get('share_sub_title',"")
            dec = jo.get('evaluate',"")
            areas = ""
            remark = ""
            try:
                areas = jo['areas'][0]['name']
            except Exception as e:
                pass
            try:
                remark = jo['new_ep']['desc']
            except Exception as e:
                pass
            vod = {
                "vod_id":id, #TODO
                "vod_name":title,
                "vod_pic":pic,#TODO
                "type_name":typeName,
                "vod_year":"",
                "vod_area":areas,
                "vod_remarks":remark,
                "vod_actor":"",
                "vod_director":"",
                "vod_content":dec
            }
            # ja = jo['episodes']
            # playUrl = ''
            # for tmpJo in ja:
            #     eid = tmpJo['id']
            #     cid = tmpJo['cid']
            #     part = tmpJo['title'].replace("#", "-")
            #     playUrl = playUrl + '{}${}/{}/{}_{}#'.format(part,FROM_BILIBILI,season_id,eid, cid)
            vod['vod_play_from'] = 'B站影视'
            # vod['vod_play_url'] = playUrl
            return vod
        except Exception as e:
            print("[E] _get_season_VideoList :",e)
            self.globalState.setError(season_id)
            return None


    